#include "rfb_data_process.h"
#include "encoding_tight.h"
#include "encodings.h"
#include "message_type.h"
#include "ffmpeg/raw2mp4.h"

#include <functional>
#include <iostream>

#include <arpa/inet.h>

#include <QDebug>
#include <QThread>
#include <QImage>

RfbDataProcesser::RfbDataProcesser()
    : m_thread(std::bind(&RfbDataProcesser::run, this))
    , m_clientNeedRecive(true)
{
}

RfbDataProcesser::~RfbDataProcesser()
{
}

void RfbDataProcesser::writeServerData(const char *data, int len)
{
    m_serverStream.write(data, len);
}

void RfbDataProcesser::writeClientData(const char *data, int len)
{
    if (m_clientNeedRecive.load()) {
        m_clientStream.write(data, len);
    }
}

void RfbDataProcesser::run()
{
    // TODO:流读取应该在有数据情况下触发，不应该循环读取，会占用cpu，需要验证readsome是否会阻塞读取？还是说用read会阻塞读取
    // 1.获取服务器发送的rfb版本信息
    s_rfbHeader header;
    m_serverStream.read((char *)&header, sizeof(header));
    // 2.获取客户端发送的rfb版本信息
    m_clientStream.read((char *)&header, sizeof(header));
    // 3.获取服务端支持的认证类型
    uint8_t securityNum = 0;
    m_serverStream.read((char *)&securityNum, sizeof(securityNum));
    if (securityNum > 0) {
        uint8_t *securitys = new uint8_t[securityNum];
        m_serverStream.read((char *)securitys, securityNum);
        delete[] securitys;
    }
    // 4.客户端选择的认证类型（1个字节）
    m_clientStream.read((char *)&securityNum, sizeof(securityNum));
    // 5.服务端认证结果
    uint32_t securityResult = 0;
    m_serverStream.read((char *)&securityResult, sizeof(securityResult));
    if (0 != securityResult) {
        qInfo() << "认证失败";
        return;
    }
    // 6.客户端初始化(0x00)
    m_clientStream.read((char *)&securityNum, sizeof(securityNum));
    // 7.服务器初始化数据
    RFBServerInitStruct rfbServerInitStruct;
    m_serverStream.read((char *)&rfbServerInitStruct, sizeof(rfbServerInitStruct));
    qInfo() << rfbServerInitStruct.fbWidth << rfbServerInitStruct.fbHeight;
    rfbServerInitStruct.fbWidth = ntohs(rfbServerInitStruct.fbWidth);
    rfbServerInitStruct.fbHeight = ntohs(rfbServerInitStruct.fbHeight);
    rfbServerInitStruct.fbPixel.red_max = ntohs(rfbServerInitStruct.fbPixel.red_max);
    rfbServerInitStruct.fbPixel.green_max = ntohs(rfbServerInitStruct.fbPixel.green_max);
    rfbServerInitStruct.fbPixel.blue_max = ntohs(rfbServerInitStruct.fbPixel.blue_max);
    rfbServerInitStruct.fbNameLength = ntohl(rfbServerInitStruct.fbNameLength);
    char *serverName = new char[rfbServerInitStruct.fbNameLength + 1];
    memset(serverName, 0, rfbServerInitStruct.fbNameLength + 1);
    m_serverStream.read(serverName, rfbServerInitStruct.fbNameLength);
    qInfo() << rfbServerInitStruct.fbWidth << rfbServerInitStruct.fbHeight << serverName;

    // 处理下客户端信息
    m_clientNeedRecive.store(false);
    // TODO:中间的先略过，处理服务器FramebufferUpdate数据

    // TODO:此处图片格式需要根据rfbServerInitStruct.fbPixel来设置
    QImage image(rfbServerInitStruct.fbWidth, rfbServerInitStruct.fbHeight, QImage::Format_RGBA8888);
    Raw2Mp4Tool raw_mp4_tool(rfbServerInitStruct.fbWidth, rfbServerInitStruct.fbHeight);
    if (!raw_mp4_tool.open("/home/yang/Desktop/test.mp4")) {
        qInfo() << "open raw to mp4 file:/home/yang/Desktop/test.mp4 failed!";
        return;
    }

    encoding::Tight::Encoder tightEncoder;
    while (true) {
        uint8_t msgType = 0;
        m_serverStream.read((char *)&msgType, sizeof(msgType));

        if (0 != msgType) {
            // TODO:这种信息也要处理，过滤掉该消息的后续信息
            switch (msgType) {
            case ServerCutTextMsg: {
                char *data = new char[3];
                m_serverStream.read(data, 3);
                delete[] data;
                uint32_t len;
                m_serverStream.read((char *)&len, sizeof(len));
                std::cout << len << " " << ntohl(len) << std::endl
                          << std::flush;
                len = ntohl(len);
                data = new char[len];
                m_serverStream.read(data, len);
                delete[] data;
                continue;
            } break;
            default: {
                qInfo() << "非更新信息" << msgType;
                goto end2;
            }
            }
            break;
        }

        FramebufferUpdate update;
        m_serverStream.read((char *)&update, 3);
        // TODO:验证过程中发现有时需要转换，有时不需要比较奇怪
        // update.number = ntohs(update.number);
        // qInfo() << "rec num:" << update.number << ntohs(update.number);
        for (int i = 0; i < update.number; i++) {
            FramebufferUpdateRectangle rect;
            m_serverStream.read((char *)&rect, sizeof(rect));
            rect.xPos = ntohs(rect.xPos);
            rect.yPos = ntohs(rect.yPos);
            rect.width = ntohs(rect.width);
            rect.height = ntohs(rect.height);
            rect.encodingType = ntohl(rect.encodingType);
            // printUpdateRectangleInfo(rect);
            switch (rect.encodingType) {
            case EncRaw: {
                int total = rect.width * rect.height * rfbServerInitStruct.fbPixel.bpp;
                char *rawData = new char[total];
                m_serverStream.read(rawData, total);
                int index = 0;
                for (int y = rect.yPos; y < rect.yPos + rect.height; y++) {
                    for (int x = rect.xPos; x < rect.xPos + rect.width; x++) {
                        image.setPixelColor(x, y, QColor(rawData[index], rawData[index + 1], rawData[index + 2]));
                        index += 3;
                    }
                }
            } break;
            case EncCopyRect: {
                uint16_t src_xPos, src_yPos;
                m_serverStream.read((char *)&src_xPos, sizeof(src_xPos));
                m_serverStream.read((char *)&src_yPos, sizeof(src_yPos));
                src_xPos = ntohs(src_xPos);
                src_yPos = ntohs(src_yPos);
                for (int y = rect.yPos; y < rect.yPos + rect.height; y++) {
                    for (int x = rect.xPos; x < rect.xPos + rect.width; x++) {
                        image.setPixelColor(x, y, image.pixelColor(src_xPos + x - rect.xPos, src_yPos + y - rect.yPos));
                    }
                }
            } break;
            case EncTight: {
                auto rawData = tightEncoder.readData(m_serverStream, rect, rfbServerInitStruct.fbPixel);
                if (rawData.size()) {
                    int index = 0;
                    for (int y = rect.yPos; y < rect.yPos + rect.height; y++) {
                        for (int x = rect.xPos; x < rect.xPos + rect.width; x++) {
                            image.setPixelColor(x, y, QColor(rawData[index], rawData[index + 1], rawData[index + 2]));
                            index += 3;
                        }
                    }
                }
            } break;
            case EncCursor: {
                readCursorTypeData(rect, rfbServerInitStruct.fbPixel.bpp);
            } break;
            case EncEndOfContinuousUpdates:
            case 181:
            case 186:
            case 139: {
            } break;
            default: {
                std::cout << i << " rect.encodingType:" << std::hex << (rect.encodingType) << std::endl
                          << std::flush;
                goto end2;
            } break;
            }
        }
    end:
        raw_mp4_tool.write(image.bits(), image.sizeInBytes());
    }
end2:
    raw_mp4_tool.close();
}

void RfbDataProcesser::printUpdateRectangleInfo(FramebufferUpdateRectangle rect)
{
    qInfo() << "xPos:" << rect.xPos << "yPos:" << rect.yPos << "width:" << rect.width << "height:" << rect.height << "encodingType:" << rect.encodingType;
}

void RfbDataProcesser::readCursorTypeData(FramebufferUpdateRectangle rect, uint8_t bpp)
{
    uint8_t bytesPixel = bpp / 8;
    uint32_t dataSize = rect.width * rect.height * bytesPixel;
    char *data = new char[dataSize];
    m_serverStream.read(data, dataSize);
    uint8_t mask = ((rect.width + 7) / 8) * rect.height;
    char *maskData = new char[mask];
    m_serverStream.read(maskData, mask);
    delete[] data;
    delete[] maskData;
}